package comms;

import java.io.IOException;

import javax.microedition.io.Connector;
import javax.wireless.messaging.*;

import ap.AgentPkt;
import agent.*;

/**
 * A class which represents a connection which uses MMS messages in order
 * to either send or receieve mobile agents.
 * <p>
 * Runs in a seperate Thread if listening for connections otherwise waits for
 * send or close methods to be called.
 * 
 * @author Kevin
 * @see AgentConnection
 */
public class MMSAgentConnection extends Thread implements AgentConnection {
	
	//The MIME type for the MMS messages.
	private static final String PLAIN_TEXT = "text/plain";
	
	//The Content descriptor for the MMS messages.
	private static final String STD_NAME = "mob_agent";
	
	//The Content File descriptor for the MMS messages.
	private static final String STD_FILE = "/mob_agent.txt";
	
	//The Encoding type for the MMS messages.
	private static final String STD_ENCODING = "UTF-8";
	
	//The Subject for the MMS messages.
	private static final String STD_SUBJECT = "MobileAgentEncoded";
	
	//The status of this connection as defined in the AgentConnection interface.
	private int connectionStatus;
	
	//Whether this object is listening for connections of not.
	private boolean listening;
	
	//The ConnectionManager object which created this object.
	private ConnectionManager owner;
	
	//The current MessageConnection for this object.
	private MessageConnection currentConnection;
	
	/**
	 * Constructor for this class. Determines whether to create an object for
	 * sending or listening based on the length of the address parameter.
	 * <p>
	 * If listening; starts a new Thread otherwise doesn't.
	 * 
	 * @param manager The ConnectionManager which created this object.
	 * @param address The address to send to; or "" if a receiving object.
	 * @param appID The MMS Application ID to assign to this connection.
	 */
	public MMSAgentConnection(ConnectionManager manager, String address, String appID) {
		this.owner = manager;
		this.currentConnection = null;
		
		try {
			this.currentConnection = (MessageConnection)Connector.open("mms://" + address + ":" + appID);
		} catch (IOException e) {
			System.err.println(e.getMessage());
			e.printStackTrace();
		}
		
		if (address.length() > 0) {
			this.connectionStatus = AgentConnection.SENDER_ONLY;
			this.listening = false;
		} else {
			this.connectionStatus = AgentConnection.RECEIVER_ONLY;
			this.listening = true;
			
			if (this.currentConnection != null) this.start();
		}
		
	}
	
	/**
	 * Run method for this Thread; constantly waits for incoming MMS messages until
	 * such time as the closeConnection method for this object is called.
	 * <p>
	 * Uses the Subject field to determine if the message is intended for this application.
	 * 
	 */
	public void run() {
		while (listening && ((this.connectionStatus == AgentConnection.RECEIVER_ONLY) || (this.connectionStatus == AgentConnection.SENDER_RECEIVER))) {
			try {
				MultipartMessage message = (MultipartMessage)this.currentConnection.receive();
				if (message.getSubject().equals(STD_SUBJECT))
					this.owner.agentReceived(message.getMessageParts()[0].getContent(), message.getMessageParts()[0].getContent().length);
			} catch (Exception e) {
				System.err.println(e.getMessage());
				e.printStackTrace();
			}
		}
	}
	
	/**
	 * Sends an agent in the form of an AgentPkt object via the current connection.
	 * 
	 * @param agentPack The AgentPkt for the agent concerned.
	 * @return True if send successful; false otherwise.
	 * @throws AgentConnectionException If connection could not be opened.
	 */
	public boolean sendAgent(AgentPkt agentPack) throws AgentConnectionException {
		MultipartMessage message;
		MessagePart data;
		boolean sent = false;
		
		if ((this.connectionStatus == AgentConnection.RECEIVER_ONLY) ||
				(this.connectionStatus == AgentConnection.CLOSED))
				throw new AgentConnectionException("Connection closed or not in send mode.");
		
		message = (MultipartMessage)this.currentConnection.newMessage("multipart");
		message.setHeader("X-Mms-Priority", "high");
		message.setSubject(STD_SUBJECT);
		
		try {
			data = new MessagePart(agentPack.getAgentString().getBytes(), 0, agentPack.getAgentString().length(), PLAIN_TEXT, STD_NAME, STD_FILE, STD_ENCODING);
			message.addMessagePart(data);
			this.currentConnection.send(message);
			sent = true;
		} catch (Exception e) {
			System.err.println(e.getMessage());
			e.printStackTrace();
		}
				
		return sent;
	}
	
	/**
	 * Sends an agent via the current connection.
	 * 
	 * @param agent The agent to be sent.
	 * @return True if send successful; false otherwise.
	 * @throws AgentConnectionException If connection could not be opened.
	 */
	public boolean sendAgent(Agent blah) {
		//TODO: Implement.
		return true;
	}

	/**
	 * Returns the saved status value of the connection as indicated by the static final
	 * integer values already defined.
	 * 
	 * @return The status of the connection as indicated by the static constants above.
	 */
	public int getStatus() {
		return this.connectionStatus;
	}

	/**
	 * Attempts to close the current connection as soon as possible.
	 *
	 */
	public void closeConnection() {
		this.listening = false;
		this.connectionStatus = AgentConnection.CLOSED;
		
		try {
			this.currentConnection.close();
		} catch (IOException ioe) {
			System.err.println(ioe.getMessage());
			ioe.printStackTrace();
		}
	}
}